package json;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import util.PythonUtil;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 格式化(美化)大json文件
 * 有个部门人员全量同步的三方接口，返回的json数据100多M，postman无法格式化，保存结果为文件，用idea也打不开
 * 调整idea的参数，打开了文件，但是不能格式化
 * Idea工具类文件大小限制导致无法解释该类问题 https://blog.csdn.net/qq_30019911/article/details/80162431
 * 打开 help -> edit custom properties选项(路径如上截图), 如果本机没有这个文件会提示要先创建
 * 点击create按钮之后在文件中添加如下语句(默认值2500即2.56m, 也可以自己设置一个值, 单位是k)
 * idea.max.intellisense.filesize=999999
 * webstorm不设置也可以以只读模式打开，而且没有idea那样卡顿
 * <p>
 * JSON大文件格式化
 * https://www.jianshu.com/p/9e94126a2987
 * https://www.jb51.net/article/210525.htm
 * https://github.com/xsank/cabbird/blob/master/recursive/json_formatter.py
 */
public class ParseJsonUtil {

    public static void main(String[] args) {
        // json文件存放目录
        String fileDir = "D:\\export_tmp";
        // json文件名称，不带.json后缀
        String fileName = "用户部门数据_" + System.currentTimeMillis();
        // 生成json文件
        genJsonFile(fileDir, fileName);

        // json文件路径
        String jsonFilePath = fileDir + File.separator + fileName + ".json";
        // 格式化json文件（java代码）
        parseJsonFile(jsonFilePath, fileDir, fileName + "_java");

        // 格式化json文件（Python脚本）
        parseJsonByPython(jsonFilePath, null);

        /*Map<String, Object> map = genJsonString(2, 3);
        // 生成json字符串
        String jsonString = JSONObject.toJSONString(map);
        System.out.println(jsonString);

        jsonString = buildJson(jsonString);
        System.out.println(jsonString);*/
    }

    /**
     * 格式化json文件（java代码）
     *
     * @param filePath json文件地址
     */
    public static void parseJsonByPython(String filePath, String targetFilePath) {
        // 解释器
        String exe = "python";
        // python脚本路径，建议使用绝对路径
        // String command = "E:\\work\\项目\\java-learn-demo\\java-tools\\src\\main\\java\\json\\parseJson.py";
        String command = "java-tools\\src\\main\\java\\json\\parseJson.py";
        String sourceFilePath = filePath;
        if (targetFilePath == null) {
            targetFilePath = filePath.substring(0, filePath.lastIndexOf(".")) + "_python.json";
        }
        // 组合成一个字符串数组
        String[] cmdArr = new String[]{exe, command, sourceFilePath, targetFilePath};

        // 执行python脚本
        PythonUtil.executePythonScript(cmdArr);

    }

    /**
     * alibaba.fastjson格式化json字符串
     *
     * 使用Fastjson/Gson/Jackson美化json字符串格式 https://blog.51cto.com/hspbc/5052416
     * 利用fastjson美化json串的方法 https://blog.csdn.net/u010251278/article/details/91992618
     *
     * @param text
     * @return
     */
    private static String buildJson(String text) {
        // 可以直接在这里打断点debug，查看变量，即json数据的结构
        Object jsonobj = JSON.parse(text, Feature.AllowSingleQuotes);

        String jsonText = JSON.toJSONString(jsonobj, SerializerFeature.WriteMapNullValue, SerializerFeature.PrettyFormat);
        return jsonText;
    }

    /**
     * 生成.json格式文件
     *
     * @param fileDir  文件保存目录
     * @param fileName json文件名称，不带.json后缀
     */
    public static void genJsonFile(String fileDir, String fileName) {
        Map<String, Object> map = genJsonString(3000, 200000);

        // 生成json字符串
        String jsonString = JSONObject.toJSONString(map);
        createJsonFile(jsonString, fileDir, fileName);
    }

    /**
     * 生成json字符串
     * @param deptCount 要生成的部门数量
     * @param userCount 要生成的用户数量
     */
    private static Map<String, Object> genJsonString(int deptCount, int userCount) {
        Map<String, Object> map = new HashMap<>();
        List<JSONObject> depts = new ArrayList();
        for (int i = 0; i < deptCount; i++) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("id", i);
            jsonObject.put("name", "部门名称_" + i);
            jsonObject.put("deptCode", "部门编码_" + i);
            jsonObject.put("deptName", "部门名称_" + i);
            jsonObject.put("parentDeptCode", "父部门编码_" + i);
            jsonObject.put("deptFullName", "部门全称\\部门全称\\部门全称_" + i);
            depts.add(jsonObject);
        }
        map.put("depts", depts);

        List<JSONObject> users = new ArrayList();
        for (int i = 0; i < userCount; i++) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("id", i);
            jsonObject.put("name", "姓名_" + i);
            jsonObject.put("gh", "工号_" + i);
            jsonObject.put("sex", "性别_" + i);
            jsonObject.put("phone", "电话电话电话_" + i);
            jsonObject.put("headImage", "头像_" + i);
            jsonObject.put("deptCode", "部门编码_" + i);
            jsonObject.put("deptName", "部门名称_" + i);
            jsonObject.put("deptFullName", "部门全称\\部门全称\\部门全称_" + i);
            jsonObject.put("email", "邮箱_" + i);
            jsonObject.put("address", "地址地址地址地址地址_" + i);
            users.add(jsonObject);
        }
        map.put("users", users);
        return map;
    }

    /**
     * 生成.json格式文件
     * https://blog.csdn.net/J_M_S_H_T/article/details/126560693
     *
     * @param jsonString json字符串
     * @param fileDir    文件保存目录
     * @param fileName   json文件名称，不带.json后缀
     * @return
     */
    public static boolean createJsonFile(String jsonString, String fileDir, String fileName) {
        // 标记文件生成是否成功
        boolean flag = true;

        // 拼接文件完整路径
        String fullPath = fileDir + File.separator + fileName + ".json";

        // 生成json格式文件
        try {
            // 保证创建一个新文件
            File file = new File(fullPath);
            if (!file.getParentFile().exists()) { // 如果父目录不存在，创建父目录
                file.getParentFile().mkdirs();
            }
            if (file.exists()) { // 如果已存在,删除旧文件
                file.delete();
            }
            file.createNewFile();

            // 将格式化后的字符串写入文件
            Writer write = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
            write.write(jsonString);
            write.flush();
            write.close();
            flag = true;
        } catch (Exception e) {
            flag = false;
            e.printStackTrace();
        }

        // 返回是否成功的标记
        return flag;
    }

    /**
     * 格式化json文件（java代码）
     *
     * @param jsonFilePath json文件地址
     */
    public static boolean parseJsonFile(String jsonFilePath, String fileDir, String fileName) {
        // 标记文件生成是否成功
        boolean flag = true;

        // 生成json格式文件
        try {
            // 保证创建一个新文件
            File file = new File(jsonFilePath);
            if (!file.exists()) {
                System.out.println("json文件不存在");
                return false;
            }
            String jsonString = readFileContent(file);
            if (jsonString == null) {
                System.out.println("文件内容为null");
                return false;
            }

            // 格式化json字符串方式一 抄的的工具类
            // jsonString = JsonFormatTool.formatJson(jsonString);
            // 格式化json字符串方式二 fastjson提供的方法
            jsonString = buildJson(jsonString);

            createJsonFile(jsonString, fileDir, fileName);
            flag = true;
        } catch (Exception e) {
            flag = false;
            e.printStackTrace();
        }

        // 返回是否成功的标记
        return flag;
    }

    /**
     * 读取文件内容
     * @param file
     */
    public static String readFileContent(File file) {
        BufferedReader reader = null;
        StringBuffer sbf = new StringBuffer();
        try (InputStreamReader in = new InputStreamReader(new FileInputStream(file),"UTF-8");) {
            reader = new BufferedReader(in);
            String tempStr;
            while ((tempStr = reader.readLine()) != null) {
                sbf.append(tempStr);
            }
            reader.close();
            return sbf.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
        return sbf.toString();
    }

}
